{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "231d6e71",
   "metadata": {},
   "source": [
    "# 基于高斯玻色采样的聚类算法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2e9e164",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T03:30:38.517625Z",
     "start_time": "2024-06-18T03:30:38.513818Z"
    }
   },
   "source": [
    "聚类算法[1]是一种无监督学习算法，用于将数据集中的对象分组成为多个类别（或簇），使得同一类别内的对象彼此相似，而不同类别之间的对象则不相似。聚类算法的目标是发现数据中的内在结构，而无需预先标记数据。常见的经典聚类算法有K均值聚类 (K-Means Clustering), 密度聚类 (DBSCAN)， 谱聚类 (Spectral Clustering)等方法。 "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb36693d",
   "metadata": {},
   "source": [
    "K均值聚类算法的核心是寻找聚类的中心， 首先随机选择K个中心点，然后计算每个数据点和中心点的距离，将其分类为最接近中心点$\\mu_j$的组， 全部计算完成后将每个组的中心$\\mu_j$重新计算，然后重复迭代这些步骤直到中心点不再更新就完成了聚类过程。\n",
    "密度聚类算法[2]能够识别出具有不同密度区域的数据点，并将它们分组为不同的簇。DBSCAN 的核心思想是根据数据点的密度来确定簇，而不是依赖于事先设定的簇的数量，DBSCAN使用的方法很简单，它任意选择一个没有类别的核心对象作为种子，然后找到所有这个核心对象能够密度可达的样本集合，即为一个聚类簇。\n",
    "接着继续选择另一个没有类别的核心对象去寻找密度可达的样本集合，这样就得到另一个聚类簇 （这样的得到都肯定是密度相连的），一直运行到所有核心对象都有类别为止。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6f3af0c",
   "metadata": {},
   "source": [
    "与此同时，高斯玻色采样也可以应用于聚类算法[3]， 根据[GBS稠密子图案例](../dense_graph/gbs_dense_graph_problem.ipynb)的讨论可知高斯玻色采样用于图问题时， 图密度较大的子图对应的样本往往有较大概率被采到， 这些子图的节点对应的数据点就属于同一个聚类，因此通过这种方法就完成了一次聚类，然后将这个聚类对应的子图移除，继续高斯玻色采样过程，可以持续的找到多个不同的聚类。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a425f919",
   "metadata": {},
   "source": [
    "# 算法说明"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce1cc10d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-17T09:15:32.374588Z",
     "start_time": "2024-06-17T09:15:32.351137Z"
    }
   },
   "source": [
    "基于高斯玻色采样的聚类算法如下\n",
    "1. 通过分类数据集构造邻接矩阵A,\n",
    "设置一个阈值距离$d_0$以及距离度量 $D_{ij}=d(x_i,x_j)$, 邻接矩阵A的构造如下\n",
    "\n",
    "$$A_{ij} = \\begin{cases} 1 & D_{ij}\\le d_0 \\\\\n",
    "0 &  D_{ij}>d_0 \\end{cases} $$\n",
    "\n",
    "2. 将邻接矩阵A放入高斯玻色采样设备中进行N次采样。\n",
    "\n",
    "3. 挑选出子图对应的采样结果，即只包含0，1的样本，计算每个子图密度，\n",
    "设置一个阈值图密度$D_0$, 挑选出最大的子图，如果图密度大于 $D_0$\n",
    "则找到一个聚类，移除子图及其节点， 更新邻接矩阵A。 否则降低阈值图密度 $D_0$，回到2\n",
    "\n",
    "4. 当节点数剩余较少时停止迭代，完成聚类过程。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3089448",
   "metadata": {},
   "source": [
    "# 代码演示"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8160dda",
   "metadata": {},
   "source": [
    "首先导入相关库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f4cd33b4",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:32:52.896887Z",
     "start_time": "2024-07-11T07:32:50.114945Z"
    }
   },
   "outputs": [],
   "source": [
    "import deepquantum.photonic as dqp\n",
    "import matplotlib.pyplot as plt \n",
    "import networkx as nx\n",
    "import numpy as np\n",
    "import itertools\n",
    "import torch "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5b087f0",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T05:53:11.980966Z",
     "start_time": "2024-06-18T05:53:11.977924Z"
    }
   },
   "source": [
    "这里采用西瓜数据集进行聚类， 每一个数据点包含密度和含糖率两个特征， 分别对应data_ws第一行和第二行。 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "50735ed2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:32:53.880374Z",
     "start_time": "2024-07-11T07:32:53.874393Z"
    }
   },
   "outputs": [],
   "source": [
    "data_ws = np.array([[0.697,0.774,0.634,0.608,0.556,0.393,0.451,0.427,0.666,0.243,0.245,0.343,0.639,0.657,0.725,0.593, 0.6223, 0.75 ],\n",
    "       \n",
    "                    [0.46,0.376,0.264,0.318,0.215,0.237,0.149,0.211,0.091,0.267,0.057,0.099,0.161,0.198,0.445,0.082, 0.062,0.405 ]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e112a45c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T06:21:48.709659Z",
     "start_time": "2024-06-18T06:21:48.705876Z"
    }
   },
   "source": [
    "<div style=\"margin-right: 15px; border-radius: 10px; background-color: rgb(255， 255， 255); text-align: center;\">\n",
    "    <img src=\"./fig/f1.png\" width=\"60%\"/>\n",
    "    <p style=\"padding: 10px; font-size: small; text-align: center; line-height: 0%;\">\n",
    "        <b>\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6081192f",
   "metadata": {},
   "source": [
    "选择合适的距离将数据点映射成对应图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9f7000e7",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:32:55.556945Z",
     "start_time": "2024-07-11T07:32:55.538010Z"
    }
   },
   "outputs": [],
   "source": [
    "def distance(p1, p2):\n",
    "    \"\"\"Euclidean distance of point1 and point 2\"\"\"\n",
    "    dis = np.sqrt(sum(abs(p1-p2)**2))\n",
    "    return dis\n",
    "\n",
    "def construct_adj_mat(data, d_0, dis_func):\n",
    "    \"\"\"Construct the adjacent matrix for the given data\"\"\"\n",
    "    num_data = data.shape[-1]\n",
    "    a = np.zeros([num_data, num_data])\n",
    "    for i in itertools.combinations(range(num_data), 2):\n",
    "        dis = dis_func(data[:,i[0]], data[:,i[1]])\n",
    "        if dis <=d_0:\n",
    "            a[i] = 1\n",
    "    return a + a.transpose()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4a279702",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:32:56.125428Z",
     "start_time": "2024-07-11T07:32:56.115463Z"
    }
   },
   "outputs": [],
   "source": [
    "#构造邻接矩阵\n",
    "a = construct_adj_mat(data_ws, 0.2, distance)\n",
    "g = nx.from_numpy_array(a)\n",
    "# nx.draw(g,  pos=data_ws2.transpose(),with_labels=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75615fe7",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T06:29:48.166896Z",
     "start_time": "2024-06-18T06:29:48.162213Z"
    }
   },
   "source": [
    "<div style=\"margin-right: 15px; border-radius: 10px; background-color: rgb(255， 255， 255); text-align: center;\">\n",
    "    <img src=\"./fig/f2.png\" width=\"50%\"/>\n",
    "    <p style=\"padding: 10px; font-size: small; text-align: center; line-height: 0%;\">\n",
    "        <b>\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "331896ff",
   "metadata": {},
   "source": [
    "开始第一轮寻找较大的稠密子图进行聚类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "1951337e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:32:58.043921Z",
     "start_time": "2024-07-11T07:32:57.490401Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "|001110001000110110>\n",
      "tensor([[ 2,  3,  4,  8, 12, 13, 15, 16]])\n",
      "[tensor([2]), 0.8571428571428571]\n"
     ]
    }
   ],
   "source": [
    "gbs = dqp.GBS_Graph(adj_mat=a, cutoff=2)\n",
    "state = gbs()\n",
    "sample_re = dqp.utils.load_sample('clustering')\n",
    "\n",
    "g_density = gbs.graph_density(g, sample_re)\n",
    "d_0 = 0.8 #设置阈值图密度\n",
    "max_node = 12 # 从较大的子图开始寻找\n",
    "flag = False\n",
    "while not flag:\n",
    "    for i in sample_re:\n",
    "        if sum(i.state)==max_node and g_density[i][1]>d_0:\n",
    "            print(i)\n",
    "            target = torch.nonzero(i.state).mT\n",
    "            print(target)\n",
    "            print(g_density[i])\n",
    "            flag = True\n",
    "    max_node = max_node - 2\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99aff812",
   "metadata": {},
   "source": [
    "这里成功的找到了第2，3，4，8，12，13，15，16个数据对应的稠密子图，也就是说这些数据应该被聚类在一起"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f7af7c0",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-17T08:38:10.983545Z",
     "start_time": "2024-06-17T08:38:10.958730Z"
    }
   },
   "source": [
    "2. 现在将第一轮找到的聚类节点移除，开始第二轮寻找较大的稠密子图进行聚类"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4aa6affa",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T06:32:36.151070Z",
     "start_time": "2024-06-18T06:32:36.146731Z"
    }
   },
   "source": [
    "<div style=\"margin-right: 15px; border-radius: 10px; background-color: rgb(255， 255， 255); text-align: center;\">\n",
    "    <img src=\"./fig/f3.png\" width=\"60%\"/>\n",
    "    <p style=\"padding: 10px; font-size: small; text-align: center; line-height: 0%;\">\n",
    "        <b>\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ec4b86d2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:33:01.195285Z",
     "start_time": "2024-07-11T07:33:01.186319Z"
    }
   },
   "outputs": [],
   "source": [
    "data_ws2 = np.delete(data_ws, target, 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6471b20d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-17T08:59:27.718735Z",
     "start_time": "2024-06-17T08:59:27.711429Z"
    }
   },
   "source": [
    "3. 重复上面的高斯玻色采样过程，寻找最大的稠密子图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b82c6d7b",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:33:02.025128Z",
     "start_time": "2024-07-11T07:33:02.018147Z"
    }
   },
   "outputs": [],
   "source": [
    "a_2 = construct_adj_mat(data_ws2, 0.2, distance)\n",
    "g2 = nx.from_numpy_array(a_2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a42bbe5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-06-18T07:09:37.735837Z",
     "start_time": "2024-06-18T07:09:37.731093Z"
    }
   },
   "source": [
    "<div style=\"margin-right: 15px; border-radius: 10px; background-color: rgb(255， 255， 255); text-align: center;\">\n",
    "    <img src=\"./fig/f5.png\" width=\"50%\"/>\n",
    "    <p style=\"padding: 10px; font-size: small; text-align: center; line-height: 0%;\">\n",
    "        <b>\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "049ad481",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:33:04.784059Z",
     "start_time": "2024-07-11T07:33:03.459426Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "chain 1: 100%|\u001b[32m████████████████████████████\u001b[0m| 1999/1999 [00:00<00:00, 2340.56it/s]\u001b[0m\n",
      "chain 2: 100%|\u001b[32m███████████████████████████\u001b[0m| 1999/1999 [00:00<00:00, 18571.20it/s]\u001b[0m\n",
      "chain 3: 100%|\u001b[32m███████████████████████████\u001b[0m| 1999/1999 [00:00<00:00, 22207.13it/s]\u001b[0m\n",
      "chain 4: 100%|\u001b[32m███████████████████████████\u001b[0m| 1999/1999 [00:00<00:00, 22284.39it/s]\u001b[0m\n",
      "chain 5: 100%|\u001b[32m███████████████████████████\u001b[0m| 1999/1999 [00:00<00:00, 22599.80it/s]\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "gbs2 = dqp.GBS_Graph(adj_mat=a_2, cutoff=2)\n",
    "state2 = gbs2()\n",
    "sample_re2 = gbs2.measure(shots=10000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "38915891",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-11T07:33:05.445092Z",
     "start_time": "2024-07-11T07:33:05.394172Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "|0011111100>\n",
      "tensor([[2, 3, 4, 5, 6, 7]])\n",
      "[1, 0.6666666666666666]\n"
     ]
    }
   ],
   "source": [
    "g_density = gbs.graph_density(g2, sample_re2)\n",
    "d_0 = 0.6 #设置阈值图密度, 需要不断调整\n",
    "max_node = 10 # 从较大的子图开始寻找\n",
    "flag = False\n",
    "while not flag:\n",
    "    for i in sample_re2:\n",
    "        if sum(i.state)==max_node and g_density[i][1]>d_0:\n",
    "            print(i)\n",
    "            target = torch.nonzero(i.state).mT\n",
    "            print(target)\n",
    "            print(g_density[i])\n",
    "            flag = True\n",
    "    max_node = max_node - 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a3c82dc",
   "metadata": {},
   "source": [
    "这里成功的找到了处理后的第2，3，4，5，6，7个数据对应的稠密子图，也就是说这些数据应该被聚类在一起，同时剩下的四个点也应该被聚类在一起， \n",
    "因为他们对应的子图密度为1。经过上面的GBS算法我们可以看到原始数据就能够被分成3个聚类。最后的聚类结果如下。\n",
    "<div style=\"margin-right: 15px; border-radius: 10px; background-color: rgb(255， 255， 255); text-align: center;\">\n",
    "    <img src=\"./fig/f4.png\" width=\"60%\"/>\n",
    "    <p style=\"padding: 10px; font-size: small; text-align: center; line-height: 0%;\">\n",
    "        <b>\n",
    "    </p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e296591",
   "metadata": {},
   "source": [
    "# 附录"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9e68621",
   "metadata": {},
   "source": [
    "[1] S. J. Russell, P. Norvig, M.-W. Chang, J. Devlin, and\n",
    "A. Dragan, Artificial Intelligence: A Modern Approach.\n",
    "Hoboken: Pearson College Div, 4\n",
    "th ed., Nov. 2020. \n",
    "\n",
    "[2]Schubert E, Sander J, Ester M, et al. DBSCAN revisited, revisited: why and how you should (still) use DBSCAN[J]. ACM Transactions on Database Systems (TODS), 2017, 42(3): 1-21.\n",
    "\n",
    "[3]Bonaldi N, Rossi M, Mattioli D, et al. Boost clustering with Gaussian Boson Sampling: a full quantum approach[J]. arXiv preprint arXiv:2307.13348, 2023.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "dq_v3",
   "language": "python",
   "name": "dq_v3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
